﻿Imports NahRT
Imports OpenTK
Imports OpenTK.Graphics.OpenGL

Public Interface IUniform
    Sub SetupUniform()
    Sub InitUniform()
End Interface

Public Class Material
    Implements IUniform
    Public Shared MaterialObjects As New List(Of Material)

    REM Uniform name
    Private nameEmissive As String = "Emissive"
    Private nameReflectionDiffuse As String = "ReflectionDiffuse"
    Private nameReflectionSpecular As String = "ReflectionSpecular"
    Private nameSpecularShiness As String = "SpecularShiness"
    Private nameOpacity As String = "Opacity"
    Private nameReflectivity As String = "Reflectivity"

    REM Uniform ID
    Private uEmissive As Integer
    Private uReflectionDiffuse As Integer
    Private uReflectionSpecular As Integer
    Private uOpacity As Integer
    Private uSpecularShiness As Integer
    Private uReflectivity As Integer

    REM 材质
    Private emissive As Vector4 = New Vector4(0.0F, 0.0F, 0.0F, 0.0F)
    Private diffuse As Vector4 = New Vector4(1.0F, 1.0F, 1.0F, 1.0F)
    Private specular As Vector4 = New Vector4(0.0F, 0.0F, 0.0F, 1.0F)
    Private shiness As Single = 1.0F
    Private opacity As Single = 1.0F
    Private reflectivity As Single = 0.0F

    Private program As Integer
    Private name As String
    Public Sub New(program As Integer, name As String)
        Me.name = name
        Me.program = program
        MaterialObjects.Add(Me)
    End Sub

    Public Property MaterialEmissive As Vector4
        Get
            Return emissive
        End Get
        Set(value As Vector4)
            emissive = value
        End Set
    End Property

    Public Property MaterialDiffuse As Vector4
        Get
            Return diffuse
        End Get
        Set(value As Vector4)
            diffuse = value
        End Set
    End Property

    Public Property MaterialSpecular As Vector4
        Get
            Return specular
        End Get
        Set(value As Vector4)
            specular = value
        End Set
    End Property

    Public Property MaterialSpecularShiness As Single
        Get
            Return shiness
        End Get
        Set(value As Single)
            shiness = value
        End Set
    End Property

    Public Property MaterialOpacity As Single
        Get
            Return opacity
        End Get
        Set(value As Single)
            opacity = value
        End Set
    End Property

    Public Property MaterialReflectivity As Single
        Get
            Return reflectivity
        End Get
        Set(value As Single)
            reflectivity = value
        End Set
    End Property


    Public Sub InitUniform() Implements IUniform.InitUniform
        GL.UseProgram(program)
        Dim name As String = "materials[" + MaterialObjects.IndexOf(Me).ToString() + "]."
        uEmissive = GL.GetUniformLocation(program, name + nameEmissive)
        uReflectionDiffuse = GL.GetUniformLocation(program, name + nameReflectionDiffuse)
        uReflectionSpecular = GL.GetUniformLocation(program, name + nameReflectionSpecular)
        uOpacity = GL.GetUniformLocation(program, name + nameOpacity)
        uSpecularShiness = GL.GetUniformLocation(program, name + nameSpecularShiness)
        uReflectivity = GL.GetUniformLocation(program, name + nameReflectivity)
    End Sub

    Public Sub SetupUniform() Implements IUniform.SetupUniform
        GL.UseProgram(program)
        GL.Uniform4(uEmissive, MaterialEmissive.X, MaterialEmissive.Y, MaterialEmissive.Z, MaterialEmissive.W)
        GL.Uniform4(uReflectionDiffuse, MaterialDiffuse.X, MaterialDiffuse.Y, MaterialDiffuse.Z, MaterialDiffuse.W)
        GL.Uniform4(uReflectionSpecular, MaterialSpecular.X, MaterialSpecular.Y, MaterialSpecular.Z, MaterialSpecular.W)
        GL.Uniform1(uSpecularShiness, MaterialSpecularShiness)
        GL.Uniform1(uOpacity, 1, opacity)
        GL.Uniform1(uReflectivity, 1, 1.0F)
    End Sub
End Class


Public Class Light
    Implements IUniform
    Public Shared LightObjects As New List(Of Light)

    Private program As Integer
    Private name As String
    Public Sub New(program As Integer, name As String)
        Me.program = program
        Me.name = name
        LightObjects.Add(Me)
    End Sub

    Private nameLightPosition As String = "Position"
    Private nameLightDirection As String = "Direction"
    Private nameLightColor As String = "Color"
    Private nameLightAttenuation As String = "Attenuation"

    Private uLightPosition As Integer
    Private uLightColor As Integer
    Private uLightAttenuation As Integer
    Private uLightDirection As Integer

    Public Property LightPosition As Vector4
        Get
            Return position
        End Get
        Set(value As Vector4)
            position = value
        End Set
    End Property

    Public Property LightColor As Vector3
        Get
            Return color
        End Get
        Set(value As Vector3)
            color = value
        End Set
    End Property

    Public Property LightAttenuation As Vector3
        Get
            Return attenuation
        End Get
        Set(value As Vector3)
            attenuation = value
        End Set
    End Property

    Public Property LightDirection As Vector3
        Get
            Return direction
        End Get
        Set(value As Vector3)
            direction = value
        End Set
    End Property

    REM 灯光
    Private position As Vector4 = New Vector4(0.0F, 50.0F, 0.0F, 1.0F) REM 光源位置
    Private color As Vector3 = New Vector3(1.0F, 1.0F, 1.0F) REM 光源颜色 假设diffuse 和 specular的颜色一样
    Private attenuation As Vector3 = New Vector3(1.0F, 0.0F, 0.0F) REM 光源衰减 ，衰减常量，线性，指数
    Private direction As Vector3 = New Vector3(0.0F, -1.0F, 0.0F) REM 光源方向

    Public Sub InitUniform() Implements IUniform.InitUniform
        GL.UseProgram(program)
        Dim name As String = "lights[" + LightObjects.IndexOf(Me).ToString() + "]."
        uLightPosition = GL.GetUniformLocation(program, name + nameLightPosition)
        uLightColor = GL.GetUniformLocation(program, name + nameLightColor)
        uLightAttenuation = GL.GetUniformLocation(program, name + nameLightAttenuation)
        uLightDirection = GL.GetUniformLocation(program, name + nameLightDirection)
    End Sub

    Public Sub SetupUniform() Implements IUniform.SetupUniform
        GL.UseProgram(program)
        GL.Uniform4(uLightPosition, position.X, position.Y, position.Z, position.W)
        GL.Uniform3(uLightDirection, direction.X, direction.Y, direction.Z)
        GL.Uniform3(uLightColor, color.X, color.Y, color.Z)
        GL.Uniform3(uLightAttenuation, attenuation.X, attenuation.Y, attenuation.Z)

        Dim uLightCount As Integer = GL.GetUniformLocation(program, "LightCount")
        GL.Uniform1(uLightCount, LightObjects.Count)
    End Sub

    Public Sub UpdateProgramUniform(p As Integer)
        GL.UseProgram(p)
        Dim name As String = "lights[" + LightObjects.IndexOf(Me).ToString() + "]."
        uLightPosition = GL.GetUniformLocation(p, name + nameLightPosition)
        uLightColor = GL.GetUniformLocation(p, name + nameLightColor)
        uLightAttenuation = GL.GetUniformLocation(p, name + nameLightAttenuation)
        uLightDirection = GL.GetUniformLocation(p, name + nameLightDirection)

        GL.Uniform4(uLightPosition, position.X, position.Y, position.Z, position.W)
        GL.Uniform3(uLightDirection, direction.X, direction.Y, direction.Z)
        GL.Uniform3(uLightColor, color.X, color.Y, color.Z)
        GL.Uniform3(uLightAttenuation, attenuation.X, attenuation.Y, attenuation.Z)

        Dim uLightCount As Integer = GL.GetUniformLocation(p, "LightCount")
        GL.Uniform1(uLightCount, LightObjects.Count)
    End Sub
End Class

Public Structure MeshInfo
    Public localTransform As Matrix4
    Public materialID As Integer
    Public Sub New(transform As Matrix4, mtlID As Integer)
        localTransform = transform
        materialID = mtlID
    End Sub
End Structure